# MacLauncher.py
#
# Copyright (c) 2006 Michael Hobbs
#
# This file is part of iTorrent.
#
# iTorrent is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# iTorrent is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with iTorrent; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


import sys
import os
import re
import errno
import popen2
import time

from BitTorrent.RawServer import Handler

import iTorrent
import utils


OSASCRIPT = '/usr/bin/osascript'
SCRIPT = """
		tell application "System Events" to set processlist to get name of every process
		tell application "Terminal"
			activate
			if processlist contains "Terminal" then
				do script "%s"
			else
				do script "%s" in front window
			end if
		end tell
		"""

ITORRENT_HELPER = 'iTorrent-helper'  # Relative to resource directory
PING_TIMEOUT = 300.0  # seconds


def launch():
	# Check if helper program exists
	helperPath = os.path.abspath(os.path.join(os.environ['RESOURCEPATH'], ITORRENT_HELPER))
	useHelper = os.path.isfile(helperPath)
	if len(sys.argv) == 2 and sys.argv[1] == 'in-terminal':
		del sys.argv[1]
		return iTorrent.main(useHelper)
	# If there is a -psn argument, this program was invoked via the .app and we need
	# restart in a Terminal. Otherwise, continue on normally.
	if len(sys.argv) == 1 or not sys.argv[1].startswith('-psn'):
		return iTorrent.main(useHelper)
	pythonCommand = '%s in-terminal' % (escape(os.environ['EXECUTABLEPATH']))
	args = [OSASCRIPT]
	script = SCRIPT % (pythonCommand, pythonCommand)
	for line in script.split('\n'):
		args += ['-e', line.strip()]
	os.execv(args[0], args)

def startHelper(fromPort, toPort, btControl):
	pingHandler = PingHandler(btControl)
	path = os.path.abspath(os.path.join(os.environ['RESOURCEPATH'], ITORRENT_HELPER))
	for _ in range(2):
		pipe = popen2.Popen3('%s %d %d %d' % (path, fromPort, toPort, pingHandler.address[1]), False, 0)
		text = pipe.fromchild.read(5)
		if text == 'ready':
			pingHandler.start()
			return
		exitCode = os.WEXITSTATUS(pipe.wait())
		# If iTorrent-helper returns with EADDRINUSE, it may be because an old copy of
		# iTorrent-helper is still hanging around. Establishing a network connection to
		# it should be enough to kill it.
		if exitCode != errno.EADDRINUSE:
			break
		try:
			s = socket.socket()
			s.connect(('localhost', fromPort))
			s.close()
		except:
			pass
		# end try
	message = '%s: Port %d cannot be used.' % (os.strerror(exitCode), fromPort)
	if exitCode in [errno.EADDRINUSE, errno.EADDRNOTAVAIL, errno.EACCES]:
		message += '\n(Is another copy of iTorrent already running?)\n' \
			'Use --control_port option to select a different port.'
	# TODO use display here
	print message
	os.exit(1)

def escape(text):
	return re.sub(r'[\\" ()&;\]\[]', r'\\\g<0>', text)


class PingHandler(Handler):
	def __init__(self, btControl):
		super(PingHandler, self).__init__()
		self.btControl = btControl
		self.rawServer = btControl.rawServer
		self.socket = self.rawServer.create_serversocket(0, 'localhost')
		self.address = self.socket.getsockname()
		self.rawServer.start_listening(self.socket, self)
		self.lastActivity = None

	def start(self):
		self.lastActivity = time.time()
		self.checkActivity()

	def connection_made(self, socket):
		self.socket = socket
		self.socket.handler = self

	def connection_lost(self, socket):
		# TODO display an error
		print "helper ping connection lost"
		self.btControl.stop()

	def data_came_in(self, addr, datagram):
		self.lastActivity = time.time()
		if datagram == 'ping':
			self.socket.write('pong')
		# end if

	def checkActivity(self):
		if self.lastActivity and time.time() - self.lastActivity > PING_TIMEOUT:
			# TODO display an error
			print "helper ping timeout"
			self.btControl.stop()
		self.rawServer.add_task(self.checkActivity, PING_TIMEOUT)
